package com.learn.lb.zk;

import org.apache.curator.framework.api.DeleteBuilder;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

/**
 * 增删改查demo
 *
 * @author laibo
 * @since 2019/9/10 13:31
 */
public class CrudZkUtils {

    private static Logger logger = LoggerFactory.getLogger(CrudZkUtils.class);


    /**
     * 创建一个 允许所有人访问的 持久节点
     *
     * @param nodePath 节点路径
     * @param nodeData 节点数据
     */
    public static void createNode(String nodePath, String nodeData) {
        try {
             /*
                creatingParentsIfNeeded： 递归创建，如果父节点不存在，则创建父节点
                withMode：节点类型,创建永久节点
                withACL：设置权限，当前设置是所有人可访问
                forPath：设置节点路径和节点数据
             */
            CuratorSessionGenerate.getCuratorSession().create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                    .forPath(nodePath, nodeData.getBytes());
        } catch (Exception e) {
            logger.error("创建永久节点失败", e);
        }
    }

    /**
     * 创建一个容器节点
     *
     * @param nodePath 节点路径
     * @param nodeData 节点数据
     */
//    public static void createContainerNode(String nodePath, String nodeData) {
//        try {
//             /*
//                creatingParentContainersIfNeeded： 递归创建，如果父节点不存在，则创建父节点
//                withMode：节点类型,创建容器节点，当最后一个子节点删除的时候，服务器会在未来的一段时间将该容器节点删除(不是立刻删除)
//                withACL：设置权限，当前设置是所有人可访问
//             */
//            CuratorSessionGenerate.getCuratorSession().create()
//                    .creatingParentContainersIfNeeded()
//                    .withMode(CreateMode.CONTAINER)
//                    .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//                    .forPath(nodePath, nodeData.getBytes());
//        } catch (Exception e) {
//            logger.error("创建容器节点失败", e);
//        }
//    }

    /**
     * 创建一个定时节点
     *
     * @param nodePath 节点路径
     * @param nodeData 节点数据
     */
    public static void createSeqNode(String nodePath, String nodeData) {
        try {
            /*
                withTtl： 设置临时节点的有效期，单位是毫秒
                withMode：节点类型,创建定时节点，在一定的时间内,如果没有操作该节点,并且该节点没有子节点,那么服务器将自动删除该节点
                withACL：设置权限，当前设置是所有人可访问
             */
            CuratorSessionGenerate.getCuratorSession().create()
                    .withTtl(3000)
                    .creatingParentContainersIfNeeded()
                    .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
                    .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                    .forPath(nodePath, nodeData.getBytes());
        } catch (Exception e) {
            logger.error("创建定时节点失败", e);
        }
    }


    /**
     * 打印节点信息
     *
     * @param nodePath 节点路径
     */
    public static void printNodeInfo(String nodePath) {
        //存储node的stat信息
        Stat nodeStat = new Stat();
        try {
            byte[] info = CuratorSessionGenerate.getCuratorSession().getData()
                    //获取stat信息存储到stat对象
                    .storingStatIn(nodeStat)
                    .forPath(nodePath);
            logger.info("{} 节点的信息为{}", nodePath, Arrays.toString(info));
        } catch (Exception e) {
            logger.error("获取节点失败，请检查路径是否正确");
        }
    }

    /**
     * 注册节点事件监听器，该watcher只会触发一次
     *
     * @param nodePath 节点路径
     */
    public static void registerWatcher(String nodePath) {
        try {
            byte[] info = CuratorSessionGenerate.getCuratorSession().getData()
                    .usingWatcher((Watcher) event -> logger.info("watcher 触发了,event:{}", event))
                    .forPath(nodePath);
            logger.info("{} 节点的信息为{}", nodePath, Arrays.toString(info));
        } catch (Exception e) {
            logger.error("获取节点失败，请检查路径是否正确");
        }
    }

    /**
     * 更新节点数据
     *
     * @param nodePath 节点路径
     * @param newData  新值
     */
    public static void updateNodeData(String nodePath, String newData) {
        try {
            Stat stat = CuratorSessionGenerate.getCuratorSession()
                    .setData()
                    .withVersion(-1)
                    .forPath(nodePath, newData.getBytes());
            logger.info("修改之后的版本号为：{}", stat.getVersion());
        } catch (Exception e) {
            logger.error("{} 节点更新数据失败，请检查路径是否正确", nodePath);
        }
    }

    /**
     * 删除节点
     *
     * @param nodePath      节点路径
     * @param recursionFlag 是否递归删除
     */
    public static void delNode(String nodePath, boolean recursionFlag) {
        DeleteBuilder deleteBuilder = CuratorSessionGenerate.getCuratorSession()
                .delete();
        if (recursionFlag) {
            deleteBuilder.deletingChildrenIfNeeded();
        }
        try {
            deleteBuilder.forPath(nodePath);
            logger.info("删除{}节点成功", nodePath);
        } catch (Exception e) {
            logger.error("删除{}节点失败", nodePath, e);
        }
    }


    /**
     * 判断节点是否存在
     *
     * @param node 节点
     * @return true: 节点存在，false：节点不存在
     */
    public static boolean existsNode(String node) {
        try {
            Stat stat = CuratorSessionGenerate.getCuratorSession()
                    .checkExists()
                    .forPath(node);
            if (null == stat) {
                logger.info("节点不存在");
                return false;
            }
            if (stat.getEphemeralOwner() > 0) {
                logger.info("节点存在，节点类型：临时节点");
            } else {
                logger.info("节点存在，节点类型：永久节点");
            }
            return true;
        } catch (Exception e) {
            logger.error("操作失败", e);
            return false;
        }
    }

}
